package com.chunlin.enumtest;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class EnumTest {
    //Enum 类的常用方法
    //values()      以数组形式返回枚举类型的所有成员
    //ordinal()     获取枚举成员的索引位置
    //valueOf()     将普通字符串转换为枚举实例
    //compareTo()   比较两个枚举成员在定义时的顺序

    //枚举优点缺点
    //优点：
    //1. 枚举常量更简单安全 。
    //2. 枚举具有内置方法 ，代码更优雅
    //缺点：
    //1. 不可继承，无法扩展
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//        test1();
//        test2();
//        test3();
//        test4();
//        reflectPrivateConstructor();
        enumSerializationExample();
    }

    private static void enumSerializationExample() {
        Day day = Day.WEDNESDAY; // 枚举实例
        String filename = "day.ser";

        // 序列化枚举实例
        //在try语句的圆括号内声明的资源（在这个例子中是ObjectOutputStream）会在try块执行完毕后自动关闭
        // 这意味着你不需要在代码中显式调用close()方法来关闭流
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filename))) {
            out.writeObject(day);
            System.out.println("Serialized day: " + day);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 反序列化枚举实例
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename))) {
            Day deserializedDay = (Day) in.readObject();
            System.out.println("Deserialized day: " + deserializedDay);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

        //为什么这样做可以避免序列化问题？
        //实例唯一性：枚举的每个元素都是单例的，Java保证在任何情况下，一个特定的枚举值只有一个实例。这避免了序列化中可能出现的多实例问题
        //类型安全：反序列化时，不能创建不属于原始枚举定义的新值。这保护了应用程序不会由于外部数据篡改而引入无效的枚举值
    }

    private static void reflectPrivateConstructor() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<Color> colorClass = Color.class;
        //注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和Integer
        //抛出NoSuchMethodException异常,代表试图访问一个不存在的构造函数
        //因为Enum这个类的构造函数如下
        //    protected Enum(String name, int ordinal) {
        //        this.name = name;
        //        this.ordinal = ordinal;
        //    }
        //所以我们自己enum的构造函数参数的前面会自动加上两个默认的参数
        //一个是String一个是int。也就是说，这里我们正确给的是4个参数：
//        Constructor<Color> colorConstructor = colorClass.getDeclaredConstructor(String.class, Integer.class);
//        colorConstructor.setAccessible(true);
//        Color color = colorConstructor.newInstance("蓝色", 5);
//        System.out.println("通过枚举私有构造函数创建的对象: " + color);

        //Java反射的源码中有这么一段代码
        //if ((clazz.getModifiers() & Modifier.ENUM) != 0)
        //throw new IllegalArgumentException("Cannot reflectively create enum objects");
        //说明Java不支持通过反射来动态创建枚举类型的实例,枚举在这里被过滤了
        //所以枚举实现的单例模式是安全的,不能通过反射来进行创建实例等一系列操作
        Constructor<Color> colorConstructor = colorClass.getDeclaredConstructor(String.class, int.class, String.class, Integer.class);
        colorConstructor.setAccessible(true);
        Color color = colorConstructor.newInstance("blue", 4, "蓝色", 5);
        System.out.println("通过枚举私有构造函数创建的对象: " + color);
    }

    private static void test4() {
        Color black = Color.BLACK;
        System.out.println(Color.getColorEnumForKey(1));
    }

    private static void test3() {
        Test black = Test.BLACK;
        Test red = Test.RED;
        Test green = Test.GREEN;
        //就是定义顺序进行相减的值
        System.out.println(black.compareTo(red));
        System.out.println(black.compareTo(green));
        System.out.println(red.compareTo(green));
    }

    private static void test2() {
        Test[] tests = Test.values();
        for (int i = 0; i < tests.length; ++i) {
            System.out.println(tests[i] + " : " + tests[i].ordinal());
        }

        //valueOf存在的意义,因为参数是字符串,所以可以动态的获取所对应的枚举值
        System.out.println(Test.valueOf("GREEN"));
    }

    private static void test1() {
        Test enum1 = Test.BLACK;
        switch (enum1) {
            case RED -> {
                System.out.println("red");
            }
            case BLACK -> {
                System.out.println("black");
            }
            case GREEN -> {
                System.out.println("green");
            }
            default -> {
            }
        }
    }
}
